package com.easygoods.service;

import com.bestvike.stone.core.io.IOUtils;
import com.bestvike.stone.core.lang.Convert;
import com.bestvike.stone.core.lang.Environment;
import com.bestvike.stone.core.lang.StringUtils;
import com.bestvike.stone.core.serialization.XmlSerializer;
import com.bestvike.stone.core.time.DateUtils;
import com.bestvike.stone.spring.exception.BusinessException;
import com.bestvike.stone.spring.exception.ErrorCode;
import com.bestvike.stone.spring.mail.bugreport.BugReportLevel;
import com.bestvike.stone.spring.mail.bugreport.BugReportUtils;
import com.bestvike.stone.spring.redis.RedisUtils;
import com.bestvike.stone.spring.service.BaseService;
import com.easygoods.bean.PayOrderBean;
import com.easygoods.dao.MemberInfoDao;
import com.easygoods.entity.DrawCashInfo;
import com.easygoods.external.wechat.WeChatApi;
import com.easygoods.external.wechat.common.bean.WeChatColor;
import com.easygoods.external.wechat.common.response.WeChatSessionResponse;
import com.easygoods.external.wechat.enterprise.response.WeChatEnterprisePayResponse;
import com.easygoods.external.wechat.pay.bean.WeChatPayArgs;
import com.easygoods.external.wechat.pay.request.WeChatPayNotifyRequest;
import com.easygoods.external.wechat.pay.respon.WeChatOrderCloseResponse;
import com.easygoods.external.wechat.pay.respon.WeChatOrderCreateResponse;
import com.easygoods.external.wechat.pay.respon.WeChatOrderQueryResponse;
import com.easygoods.external.wechat.pay.respon.WeChatPayCoreResponse;
import com.easygoods.shiro.auth.UserAuth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

/**
 * Created by 许崇雷 on 2019-01-23.
 */
@Service
public class WeChatService extends BaseService {
    private static final String WECHAT_SESSION_PREFIX = "WECHAT:SESSION:";
    @Autowired
    private WeChatApi weChatApi;
    @Autowired
    private OrderPayService orderPayService;
    @Autowired
    private MemberInfoDao memberInfoDao;

    //支付签名
    public WeChatPayArgs signPay(String payTradeNoPre) {
        return this.weChatApi.signPay(payTradeNoPre);
    }

    //判断是否有 openid
    public boolean hasOpenId(String openId) {
        final WeChatSessionResponse sessionResponse = RedisUtils.getExpire(WECHAT_SESSION_PREFIX + openId, WeChatSessionResponse.class);
        return sessionResponse != null;
    }

    //获取微信 openid
    public WeChatSessionResponse getSession(String jscode) {
        final WeChatSessionResponse session = this.weChatApi.getSession(jscode);
        RedisUtils.set(WECHAT_SESSION_PREFIX + session.getOpenid(), session, 20, TimeUnit.MINUTES);//记录微信session
        return session;
    }

    //获取小程序码
    public void getWXACodeUnlimit(String path, Integer width, WeChatColor lineColor, HttpServletResponse response) throws IOException {
        final UserAuth userAuth = this.getAuth();
        final ResponseEntity<byte[]> responseEntity = this.weChatApi.getWXACodeUnlimit(userAuth.getMemberId(), path, width, lineColor == null, lineColor, true);
        final byte[] body = responseEntity.getBody();
        Assert.notNull(body, "微信未返回数据");
        final MediaType contentType = responseEntity.getHeaders().getContentType();
        if (contentType != null)
            response.setContentType(contentType.getType());
        response.setContentLength(body.length);
        IOUtils.write(body, response.getOutputStream());
    }

    //创建微信订单
    public WeChatOrderCreateResponse createOrder(String openId, String orderId, BigDecimal payAmount, String attach) {
        int cents = payAmount.multiply(Convert.toDecimal(100)).intValue();
        return this.weChatApi.createOrder(openId, orderId, cents, attach);
    }

    //微信支付
    public WeChatPayCoreResponse payOrder(String xml) {
        //无数据
        if (StringUtils.isEmpty(xml)) {
            BugReportUtils.sendAsync(BugReportLevel.WARN, "[支付通知接口] 被非法访问, 没有传入任何数据");
            return new WeChatPayCoreResponse(false, "非法请求, 已通知相关人员");
        }

        //格式
        WeChatPayNotifyRequest notifyRequest;
        try {
            notifyRequest = XmlSerializer.deserialize(xml, WeChatPayNotifyRequest.class);
        } catch (Exception e) {
            BugReportUtils.sendAsync(BugReportLevel.WARN, "[支付通知接口] 被非法访问, 数据格式错误" + Environment.NewLine + xml);
            return new WeChatPayCoreResponse(false, "非法请求, 已通知相关人员");
        }

        //签名
        String sign = notifyRequest.getSign();
        if (StringUtils.isEmpty(sign)) {
            BugReportUtils.sendAsync(BugReportLevel.WARN, "[支付通知接口] 被非法访问, 没有签名" + Environment.NewLine + xml);
            return new WeChatPayCoreResponse(false, "非法请求, 已通知相关人员");
        }
        String computeSign = this.weChatApi.signPay(notifyRequest);
        if (!StringUtils.equals(sign, computeSign)) {
            BugReportUtils.sendAsync(BugReportLevel.WARN, "[支付通知接口] 被非法访问, 签名错误" + Environment.NewLine + xml);
            return new WeChatPayCoreResponse(false, "非法请求, 已通知相关人员");
        }

        //有效性验证
        if (!notifyRequest.isNetSuccess()) {
            this.log.info("网络失败,返回");
            return new WeChatPayCoreResponse(false, notifyRequest.getReturnMsg());
        }
        if (!notifyRequest.isBusinessSuccess()) {
            this.log.info("业务失败,返回");
            return new WeChatPayCoreResponse(false, notifyRequest.getErrCodeDes());
        }

        //支付
        PayOrderBean payOrderBean = PayOrderBean.fromWeChatNotify(notifyRequest);
        return this.orderPayService.payOrder(payOrderBean).toWeChatPayResponse();
    }

    //关闭订单
    public WeChatOrderCloseResponse closeOrder(String orderId) {
        return this.weChatApi.closeOrder(orderId);
    }

    //查询订单状态,支付成功返回true
    public boolean queryOrder(String orderId) {
        final WeChatOrderQueryResponse response = this.weChatApi.queryOrder(orderId);
        if (!response.isPaySuccess())
            return false;

        PayOrderBean payOrderBean = PayOrderBean.fromWeChatQuery(response);
        return this.orderPayService.payOrder(payOrderBean).isSuccess();
    }

    //是否支付中或已支付.此时不允许取消订单
    public boolean isPayingOrPaid(String orderId) {
        final WeChatOrderQueryResponse response = this.weChatApi.queryOrder(orderId);
        return response.isPaySuccess() || response.isPaying();
    }

    //提现
    public void drawCash(DrawCashInfo drawCashInfo) {
        Assert.notNull(drawCashInfo, "drawCashInfo can not be null");
        final WeChatEnterprisePayResponse response = this.weChatApi.enterprisePay(drawCashInfo.getTransferOpenId(), drawCashInfo.getId(), drawCashInfo.getTransferAmount().multiply(new BigDecimal("100")).intValue(), "提现");
        if (StringUtils.isEmpty(response.getPayment_no())) {
            this.log.warn("提现交易成功,但是没有返回交易单号 drawCashId:" + drawCashInfo.getId());
            throw new BusinessException(ErrorCode.ERROR_SERVER, "提现交易失败");
        }

        //记录结果
        final int count = this.memberInfoDao.finishDrawCash(drawCashInfo.getId(), response.getPayment_no(), DateUtils.fromDateTimeString(response.getPayment_time()));
        if (count != 1) {
            this.log.error("提现已经完成,但是记录结果失败 drawCashId:" + drawCashInfo.getId());
            throw new BusinessException(ErrorCode.ERROR_SERVER, "提现操作失败");
        }
    }
}
